package controllers

import (
	"context"
	"gitverse.ru/synapse/kubelatte/pkg/logs"
	"gitverse.ru/synapse/kubelatte/pkg/util"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/event"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
	"sync"
)

type NamespaceReconciler struct {
	client.Client
	Scheme *runtime.Scheme
	once   sync.Once

	nsObj               corev1.Namespace
	deletedNamespaceObj corev1.Namespace
	initedFlag          bool
}

func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	Mux.Lock()
	defer Mux.Unlock()

	r.once.Do(func() {
		r.initedFlag = true
	})

	// Check delete event
	err := r.Get(ctx, req.NamespacedName, &corev1.Namespace{})
	if err != nil {
		if errors.IsNotFound(err) {
			return ctrl.Result{}, r.DeleteEventHndl()
		}
		return ctrl.Result{}, err
	}

	// Add or update event
	return ctrl.Result{}, r.AddOrUpdateEventHndl(ctx, req)
}

func (r *NamespaceReconciler) manageEvents() predicate.Predicate {
	return predicate.Funcs{
		CreateFunc: func(e event.CreateEvent) bool {
			eventOperation = addEvent
			return true
		},
		UpdateFunc: func(e event.UpdateEvent) bool {
			eventOperation = updateEvent
			obj := *e.ObjectNew.(*corev1.Namespace)
			if IsCreatorRole() && CheckNamespace(obj.ObjectMeta.Name, NSSelectorTI) {
				HandleUnhandledTIs(obj.ObjectMeta.Name)
			}
			if e.ObjectOld.GetResourceVersion() != e.ObjectNew.GetResourceVersion() {
				logs.Debugf("Update Namespace with name %s", obj.Name)
				return true
			}
			return false
		},
		DeleteFunc: func(e event.DeleteEvent) bool {
			eventOperation = deleteEvent
			r.deletedNamespaceObj = *e.Object.(*corev1.Namespace)
			logs.Debugf("Delete Namespace with name %s", r.deletedNamespaceObj.Name)
			return true
		},
	}
}

// SetupWithManager sets up the controller with the Manager.
func (r *NamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&corev1.Namespace{}).
		WithEventFilter(r.manageEvents()).
		Complete(r)
}

func (r *NamespaceReconciler) ProceedInstance(obj corev1.Namespace, eventName string) error {
	return util.SendEventWithErr(eventName, obj)
}

func (r *NamespaceReconciler) AddOrUpdateEventHndl(ctx context.Context, req ctrl.Request) error {
	nsObject := corev1.Namespace{}

	err := r.Get(ctx, req.NamespacedName, &nsObject)
	if err != nil {
		return err
	}

	return r.ProceedInstance(nsObject, StorageUpdateNamespaceEvent)
}

func (r *NamespaceReconciler) DeleteEventHndl() error {
	return r.ProceedInstance(r.deletedNamespaceObj, StorageDeleteNamespaceEvent)
}

func (r *NamespaceReconciler) HealthCheck() bool {
	return r.initedFlag
}
